In The Rolling Stones, Robert A. Heinlein comments:
Every technology goes through three stages: first a crudely simple and quite unsatisfactory gadget; second, an enormously complicated group of gadgets designed to overcome the shortcomings of the original and achieving thereby somewhat satisfactory performance through extremely complex compromise; third, a final proper design therefrom.
Heinlein's comment could well describe the evolution of the C language over the course of its long lifetime. This chapter discusses the three primary design features of the Java language, namely, simple, object-oriented, and familiar. The end of this chapter contains a discussion on those features that were eliminated from C and C++ in the evolution towards the Java language.
Simple
Based on the design methodology known as KISS, simplicity is one of the Java language's overriding design goals from a programming standpoint. Simplicity and removal (from C++-based languages) of many dubious "features" keep the Java language relatively small and reduce the programmer's burden in producing reliable applications. To this end, the Java language design team examined many aspects of the "modern" C and C++ languages to determine features that could be eliminated in the context of modern object-oriented programming.
Object Oriented
To stay abreast of modern software development practices, the Java language was designed to be object oriented from the ground up. The benefits of object technology are slowly becoming realized as more organizations move their applications to the distributed client-server model. The Java language goes beyond C++ in both extending the object models and removing the major complexities of C++. With the exception of its primitive data types, everything in the Java language is an object.
Familiar
Another major design goal was that the Java language look familiar to the majority of programmers in the personal computer and workstation arenas. A large fraction of today's system and application programmers are familiar with C and C++. Thus, the Java language "looks like" C++. Programmers familiar with C, Objective-C, C++, Eiffel, Ada, and related languages should find their Java language learning curve is quite short--on the order of a couple of days or so.
To illustrate the simple, object-oriented, and familiar aspects of the Java language, we follow the tradition of a long line of illustrious (and some not so illustrious) programming books by showing you the HelloWorld program. It's about the simplest program you can write that actually does something. Here's HelloWorld in the Java language.
class HelloWorld {
static public void main(String args[]) {
System.out.println("Hello world!");
}
}
This example declares a class named HelloWorld. Classes are discussed in the section on object-oriented programming, but in general we assume the reader is familiar with object technology and understands the basic notions of classes, objects, instance variables, and methods. Within the HelloWorld class, we declare a single method called main which in turn contains a single method invocation to display the string "Hello world!" on the standard output. The statement that prints "Hello world!" does so by invoking the println method of the out object, which is a class variable of the System class. That's all there is to HelloWorld.
Other than the primitive data types discussed here, everything in the Java language is an object. Even the primitive data types can be wrapped inside language-supplied objects as required. The Java language follows C and C++ fairly closely in its set of basic data types, with a couple of minor exceptions. There are only three groups of primitive data types--numeric types, Boolean types, and arrays.
Integer numeric types are 8-bit byte, 16-bit short, 32-bit int, and 64-bit long. There are no unsigned types.
Real numeric types are 32-bit float and 64-bit double. Real numeric types and their arithmetic operations are as defined by the IEEE 754 specification.
Character data is a minor departure from traditional C. The Java language uses the Unicode character set standard, so the traditional C char data type was expanded from eight to sixteen bits. If you write a declaration such as
char myChar = `Q';
you get a Unicode (16-bit unsigned character) type that's initialized to the Unicode value of the character Q.
The Boolean is a primitive type in the Java language, tacitly ratifying existing C and C++ programming practice. A boolean variable assumes the values true or false. A Java boolean is a distinct data type--unlike common C practices, a boolean can not be converted to a numeric type by casting.
Arithmetic and Relational Operators
All the familiar C and C++ operators apply in the Java language. Because the Java language lacks unsigned data types, the Java language adds the >>> operator to mean an unsigned (logical) right shift. The Java language also uses the + operator for string concatenation, discussed below.
Arrays
Arrays in the Java language are first-class language objects. A Java language array is a real object with a run-time representation. You can declare and allocate arrays of any type. To obtain multi-dimensional arrays, you allocate arrays of arrays. You declare an array of, say, Object with a declaration like this:
Object myArray[];
This code declares that myArray is an uninitialized array of Object. This variable has no storage as yet. At some future point you must allocate the amount of storage you need, say:
myArray = new Object[10];
to allocate an array of 10 Objects. To get the length of an array, add .length to the array name--myArray.length would return the number of elements in myArray. Access to elements of myArray can be performed via the normal C indexing, but all array accesses are checked to ensure that their indexes are within the range of the array. An exception will be generated if the index is outside the bounds of the array.
The C notion of a pointer to an array of memory elements has gone, and with it, the arbitrary pointer arithmetic that leads to unreliable code in C. No longer can you "walk off the end" of an array, possibly trashing memory and leading to the famous "delayed crash" syndrome, where a memory access violation today manifests itself hours or days later. Programmers can be confident that Java array checking will help lead to more robust and reliable code.
Strings
Strings are objects, not arrays of characters as in C. String objects are instances of the String class. There are actually two kinds of String objects. The String class is for read-only (immutable) objects. The StringBuffer class is for mutable String objects.
Although Strings are objects, the Java language compiler "knows" about Strings as a special syntactic feature to accommodate the syntactic convenience that C programmers have enjoyed with C-style strings. The Java language compiler understands that a string of characters enclosed in double quote signs is to be instantiated as a String object. Thus, the declaration:
String hello = "Hello world!";
instantiates an object of the String class behind the scenes, and initializes it with the character string "Hello world!".
The Java language has extended the meaning of the + operator to indicate string concatenation. Thus you can write statements like:
System.out.println("There are " + num + " characters in the file");
This code fragment concatenates the string "There are " with the result of converting the numeric value num to a string, and concatenates that with the string " characters in the file". Then it prints the result of those concatenations on the standard output.
String objects supply a length accessor method to obtain the number of characters in the string.
Multi Level Break
The Java language has no goto statement. To break or continue multiply nested loop or switch constructs, you use labels to label loop and switch constructs, and then break or continue to the label. Here's a small fragment of code from the Java language's built-in String class.
test: for (int i = fromIndex; i + max1 <= max2; i++) {
if (charAt(i) == c0) {
for (int k = 1; k<max1; k++)
if (charAt(i+k) != str.charAt(k)) {
continue test;
The continue statement that leads to test is nested two levels deep inside for statements. This Java language feature leads to considerable simplification of programming time, and major reduction in future maintenance efforts.
Memory Management and Garbage Collection
C and C++ programmers are by now accustomed to the problems of explicitly managing memory--allocating memory, freeing memory, and keeping track of what memory can be freed when. Explicit memory management has proved to be a fruitful source of bugs, crashes, memory leaks, and poor performance.
The Java language completely removes the memory management load from the programmer. C-style pointers, pointer arithmetic, malloc, and free do not exist. Automatic garbage collection is an integral part of the Java language and run-time system. Once you have allocated an object, the run-time system keeps track of the object's status and automatically reclaims memory when objects are no longer in use, freeing memory for future use.
The Java language's memory management model is based on objects and references to objects. Because the Java language has no pointers, all references to allocated storage--which in practice means references to objects--are through symbolic "handles". The Java memory manager keeps track of references to objects. When an object has no more references, the object is a candidate for garbage collection. While the Java language has a new operator to allocate space for objects, there is no explicit free function.
The Java language's memory allocation model and automatic garbage collection make your programming task easier, eliminate entire classes of bugs, and in general provide better performance than you'd obtain through explicit memory management. Here's a code fragment that illustrates when garbage collection happens. It's an example from the on-line Java language programmer's guide:
class ReverseString {
public static String reverseIt(String source) {
int i, len = source.length();
StringBuffer dest = new StringBuffer(len);
for (i = (len - 1); i >= 0; i--) {
dest.appendChar(source.charAt(i));
}
return dest.toString();
}
}
The variable dest is used as a temporary object reference during the execution of the reverseIt method. When dest goes out of scope (the reverseIt method returns), the reference to that object has gone away and it's then a candidate for garbage collection.
The Background Garbage Collector
A common criticism of garbage collection schemes is that they tend to fire up at inappropriate times, creating poor interactive response for the user.
The Java language run-time system largely solves this problem by running the garbage collector in a low priority thread. This, by the way, is just one of many examples of the synergy one obtains from the Java language's integrated multithreading capabilities--an otherwise intractable problem is solved in a simple and elegant fashion.
The typical user of the typical interactive application has many natural pauses where the user is contemplating the scene in front of them or thinking of what to do next. The Java language run-time system can take advantage of these idle periods and run the garbage collector when no other threads are competing for CPU cycles The garbage collector gathers and compacts unused memory, thereby improving the probability that adequate memory resources are available when needed during periods of heavy interactive activity.
Integrated Thread Synchronization
The Java language supports multithreading, both at the language (syntactic) level and via support from its run-time system and Thread objects. While other systems have provided facilities for multithreading (usually via "lightweight process" libraries), building multithreading support into the language provides the programmer with a much more powerful tool for easily creating thread-safe multithreaded classes. Multithreading is discussed in more detail in a separate chapter.
The Java language is object oriented. It draws on the best concepts and features of previous object-oriented languages, primarily Eiffel, SmallTalk, C++, and Objective-C.
Unfortunately, "object oriented" remains misunderstood, over-marketed, or takes on the trappings of a religion. The cynic's view of object-oriented programming is that it's just a new way to organize your source code. While there may be some merit to this view, it doesn't tell the whole story, because you can achieve results with object-oriented programming that you can't with procedural techniques.
Object Technology in the Java Language
To be truly considered "object oriented", a programming language should support at a minimum three characteristics:
ΓÇóEncapsulation--to implement information hiding and modularity
ΓÇóInheritance--code re-use and code organization
ΓÇóDynamic binding--for maximum flexibility while a program is executing.
The Java language meets these requirements nicely, and adds considerable run-time support to make your software development job easier.
At its simplest, object technology is a collection of analysis, design, and programming methodologies that focus design on modelling the characteristics and behavior of objects in the real world. True, this definition appears to be somewhat circular, so let's try to break out into clear air.